from typing import List, Optional
import time
import json

from fastapi import APIRouter, HTTPException, BackgroundTasks
from acme import messages

from app.schemas.certificates import (
    Certificate,
    CertificateAutoApplyRequest,
    CertificateCreate,
    CertificateIdRequest,
    CertificateManualApplyRequest,
    CertificateRenewRequest,
    CertificateUploadRequest,
    CertificateManualApplyResponse,
    CertificateVerifyRequest,
    CertificateStatus,
    CertificateOrderCreateRequest,
    CertificateOrderResponse,
)
from app.schemas.common import OperationResult, StandardResponse, ResponseMetadata, ErrorResponse
import uuid
from app.services.datastore import store
from app.services.acme_client import get_acme_client
from app.services.cloud_providers.factory import CloudProviderFactory
from app.schemas.dns import DNSRecordCreate, DNSRecordType

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
import logging
from app.utils.csr import generate_csr

logger = logging.getLogger(__name__)

router = APIRouter()


@router.post("/certificates", response_model=StandardResponse)
def create_certificate_order(payload: CertificateOrderCreateRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "CreateCertificateOrder"
    
    # 1. Create Certificate Record
    cert = store.create_certificate(CertificateCreate(
        common_name=payload.domains[0],
        domains=payload.domains,
        auto_renew=True
    ))
    
    # 2. Init ACME Client & Order
    try:
        acme = get_acme_client()
        acme.register_account()
            
        # Create order
        try:
            order, private_key_pem = acme.create_order(payload.domains)
        except Exception as e:
            return StandardResponse.error(
                code="ACME_001",
                message="Failed to create ACME order",
                detail=str(e),
                request_id=request_id,
                action=action
            )
        
        # Get challenges
        challenges = acme.get_challenges(order)
        domain_validations = [
            {
                "domain": c.domain,
                "record_name": c.txt_record_name,
                "record_value": c.txt_record_value,
                "authorization_url": c.authorization_url,
                "challenge_url": c.validation_url,
            }
            for c in challenges
        ]
        
        # Store order info and private key
        store.certificates.update_one(
            {"_id": store._object_id(cert.id)},
            {"$set": {
                "order_json": order.json_dumps(),
                "order_url": order.uri,
                "private_key_pem": private_key_pem,
                "authorization_urls": [
                    item["authorization_url"]
                    for item in domain_validations
                    if item.get("authorization_url")
                ],
                "domain_validations": domain_validations,
            }}
        )
        
        return StandardResponse.success(
            result=CertificateOrderResponse(
                order_id=cert.id, # Use cert ID as order ID
                status="pending",
                message="请配置 DNS TXT 记录后触发验证",
                challenges=challenges
            ).dict(),
            request_id=request_id,
            action=action
        )
    except Exception as e:
        store.delete_certificate(cert.id)
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/verify", response_model=StandardResponse)
def verify_certificate(payload: CertificateVerifyRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "VerifyCertificate"
    
    logger.info(f"[{request_id}] Verifying certificate. Order ID: {payload.order_id}, Order URL: {payload.order_url}")

    # 1. Find cert
    cert_doc = None
    if payload.order_id:
        cert_doc = store.certificates.find_one({"_id": store._object_id(payload.order_id)})
    elif payload.order_url:
        cert_doc = store.certificates.find_one({"order_url": payload.order_url})
        
    if not cert_doc:
        logger.warning(f"[{request_id}] Certificate order not found")
        return StandardResponse.error(
            code="CERT_001",
            message="Certificate order not found",
            detail=f"Order not found in database",
            request_id=request_id,
            action=action
        )
    cert = store._to_certificate(cert_doc)
    
    # Idempotency check
    if cert.status == CertificateStatus.issued:
        logger.info(f"[{request_id}] Certificate already issued: {cert.id}")
        return StandardResponse.success(
            result=cert.dict(),
            request_id=request_id,
            action=action
        )
    elif cert.status in (CertificateStatus.revoked,): # Removed failed as it might be retryable? User said failed too.
        # Let's stick to user suggestion but maybe allow retrying failed? 
        # User suggested: elif cert.status in (CertificateStatus.revoked, CertificateStatus.failed):
        # But wait, if it failed, maybe we want to retry? 
        # Let's assume revoked is final. Failed might be transient or final. 
        # For now, let's just check issued.
        pass

    try:
        acme = get_acme_client()
        acme.register_account()
        
        if not cert.order_url:
            return StandardResponse.error(
                code="CERT_002",
                message="Order URL missing",
                detail="Please re-apply the certificate",
                request_id=request_id,
                action=action
            )

        order = acme.fetch_order(cert.order_url)
        authorization_urls = extract_authorization_urls(cert_doc, order)
        
        logger.debug(f"[{request_id}] Fetched order, authorizations: {len(authorization_urls)}")
        
        if not authorization_urls:
             return StandardResponse.error(
                code="ACME_006",
                message="No authorization URLs found",
                request_id=request_id,
                action=action
            )
        
        # Answer challenges
        try:
            acme.answer_challenges(order)
        except Exception as e:
            logger.error(f"[{request_id}] Failed to answer challenges: {e}")
            return StandardResponse.error(
                code="ACME_005",
                message="Failed to answer challenges",
                detail=str(e),
                request_id=request_id,
                action=action
            )
            
        # Regenerate CSR
        csr_pem = generate_csr(cert_doc["private_key_pem"], cert.domains)

        try:
            acme.wait_for_authorizations(authorization_urls)
        except TimeoutError as exc:
            logger.error(f"[{request_id}] Authorization timeout: {exc}")
            return StandardResponse(
                ResponseMetadata=ResponseMetadata(
                    RequestId=request_id,
                    Action=action,
                    Version="v1",
                    Service="CDH"
                ),
                Result={
                    "order_url": cert.order_url,
                    "validations": cert_doc.get("domain_validations", [])
                },
                Error=ErrorResponse(
                    Code="ACME_002", 
                    Message="Authorization timeout", 
                    Detail=str(exc)
                )
            )
        except Exception as exc:
            logger.error(f"[{request_id}] Authorization failed: {exc}")
            return StandardResponse(
                ResponseMetadata=ResponseMetadata(
                    RequestId=request_id,
                    Action=action,
                    Version="v1",
                    Service="CDH"
                ),
                Result={
                    "order_url": cert.order_url,
                    "validations": cert_doc.get("domain_validations", [])
                },
                Error=ErrorResponse(
                    Code="ACME_003", 
                    Message="Authorization failed", 
                    Detail=str(exc)
                )
            )

        # Finalize
        try:
            finalized_order = acme.poll_and_finalize(order, csr_pem)
        except Exception as e:
            logger.error(f"[{request_id}] Finalization Error: {e}")
            return StandardResponse.error(
                code="ACME_004",
                message="Finalization Error",
                detail=str(e),
                request_id=request_id,
                action=action
            )
            
        # Update certificate
        store.update_certificate_status(
            cert.id, 
            CertificateStatus.issued,
            cert_pem=finalized_order.fullchain_pem,
            key_pem=cert_doc["private_key_pem"] # Use stored private key
        )
        
        final_cert = store.describe_certificate(cert.id)
        logger.info(f"[{request_id}] Certificate issued successfully: {cert.id}")
        return StandardResponse.success(
            result=final_cert.dict(),
            request_id=request_id,
            action=action
        )
        
    except Exception as e:
        logger.error(f"[{request_id}] Internal Server Error: {e}")
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/apply/auto", response_model=StandardResponse)
def auto_apply_certificate(payload: CertificateAutoApplyRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "AutoApplyCertificate"

    try:
        provider_name, access_key, secret_key = store.get_decrypted_credential(payload.credential_id)
    except KeyError:
        return StandardResponse.error(
            code="CRED_001",
            message="Credential not found",
            detail=f"Credential ID {payload.credential_id} not found",
            request_id=request_id,
            action=action
        )

    create_payload = CertificateCreate(
        credential_id=payload.credential_id,
        provider=provider_name,
        common_name=payload.domains[0],
        domains=payload.domains,
        auto_renew=True
    )
    cert = store.create_certificate(create_payload)
    
    try:
        # 2. Init ACME
        acme = get_acme_client()
        # Register (or get existing) account. 
        # This sets the account on the client for subsequent requests.
        acme.register_account()
            
        # Create order
        try:
            order, private_key_pem = acme.create_order(cert.domains)
        except Exception as e:
            store.update_certificate_status(cert.id, CertificateStatus.revoked) # or failed
            return StandardResponse.error(
                code="ACME_001",
                message="Failed to create ACME order",
                detail=str(e),
                request_id=request_id,
                action=action
            )

        challenges = acme.get_challenges(order)
        domain_validations = [
            {
                "domain": c.domain,
                "record_name": c.txt_record_name,
                "record_value": c.txt_record_value,
                "authorization_url": c.authorization_url,
                "challenge_url": c.validation_url,
            }
            for c in challenges
        ]

        # Store order info and private key
        store.certificates.update_one(
            {"_id": store._object_id(cert.id)},
            {"$set": {
                "order_json": order.json_dumps(),
                "order_url": order.uri,
                "private_key_pem": private_key_pem,
                "authorization_urls": [
                    item["authorization_url"]
                    for item in domain_validations
                    if item.get("authorization_url")
                ],
                "domain_validations": domain_validations,
            }}
        )
        
        # 3. Set DNS Records
        cloud_client = CloudProviderFactory.create(provider_name, access_key, secret_key)
        
        for chall in challenges:
            full_domain = chall.domain
            # Simple heuristic for zone: assume payload domains are zones
            zone = None
            for d in payload.domains:
                if full_domain.endswith(d):
                    zone = d
                    break
            if not zone:
                zone = full_domain.split(".", 1)[1] if full_domain.count('.') > 1 else full_domain
                
            rr = "_acme-challenge"
            prefix = full_domain[:-len(zone)-1] if len(full_domain) > len(zone) else ""
            if prefix:
                rr = f"_acme-challenge.{prefix}"
            
            rec = DNSRecordCreate(
                credential_id=payload.credential_id,
                domain=zone,
                name=rr,
                value=chall.txt_record_value,
                record_type=DNSRecordType.txt,
                ttl=600
            )
            try:
                cloud_client.create_record(rec)
            except Exception as e:
                print(f"Failed to add record: {e}")
        
        # 4. Wait for propagation
        time.sleep(15) 
        
        # Answer challenges
        acme.answer_challenges(order)
        
        # Regenerate CSR (we have private_key_pem)
        csr_pem = generate_csr(private_key_pem, cert.domains)
            
        # Finalize
        try:
            finalized_order = acme.poll_and_finalize(order, csr_pem)
        except Exception as e:
            store.update_certificate_status(cert.id, CertificateStatus.revoked) # or failed
            return StandardResponse.error(
                code="ACME_004",
                message="Finalization Error",
                detail=str(e),
                request_id=request_id,
                action=action
            )
        
        store.update_certificate_status(
            cert.id, 
            CertificateStatus.issued,
            cert_pem=finalized_order.fullchain_pem,
            key_pem=private_key_pem
        )
        
        final_cert = store.describe_certificate(cert.id)
        return StandardResponse.success(
            result=final_cert.dict(),
            request_id=request_id,
            action=action
        )
        
    except Exception as e:
        # Mark as failed?
        store.delete_certificate(cert.id) # Or keep it for debugging?
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/renew", response_model=StandardResponse)
def renew_certificate(payload: CertificateRenewRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "RenewCertificate"
    try:
        # Reuse auto apply logic?
        result = store.renew_certificate(payload)
        return StandardResponse.success(
            result=result.dict(),
            request_id=request_id,
            action=action
        )
    except Exception as e:
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/upload", response_model=StandardResponse)
def upload_certificate(payload: CertificateUploadRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "UploadCertificate"
    try:
        result = store.upload_certificate(payload)
        return StandardResponse.success(
            result=result.dict(),
            request_id=request_id,
            action=action
        )
    except Exception as e:
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/list", response_model=StandardResponse)
def list_certificates() -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "ListCertificates"
    try:
        result = store.list_certificates()
        return StandardResponse.success(
            result=[item.dict() for item in result],
            request_id=request_id,
            action=action
        )
    except Exception as e:
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/describe", response_model=StandardResponse)
def describe_certificate(payload: CertificateIdRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "DescribeCertificate"
    try:
        result = store.describe_certificate(payload.id)
        if not result:
             return StandardResponse.error(
                code="CERT_001",
                message="Certificate not found",
                detail=f"Certificate ID {payload.id} not found",
                request_id=request_id,
                action=action
            )
        return StandardResponse.success(
            result=result.dict(),
            request_id=request_id,
            action=action
        )
    except Exception as e:
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )


@router.post("/delete", response_model=StandardResponse)
def delete_certificate(payload: CertificateIdRequest) -> StandardResponse:
    request_id = str(uuid.uuid4())
    action = "DeleteCertificate"
    try:
        store.delete_certificate(payload.id)
        return StandardResponse.success(
            result=OperationResult(message="certificate deleted").dict(),
            request_id=request_id,
            action=action
        )
    except Exception as e:
        return StandardResponse.error(
            code="SYS_500",
            message="Internal Server Error",
            detail=str(e),
            request_id=request_id,
            action=action
        )

def extract_authorization_urls(cert_doc, order) -> List[str]:
    urls = []
    stored = cert_doc.get("authorization_urls") or []
    if isinstance(stored, list):
        for item in stored:
            if isinstance(item, dict):
                val = item.get("authorization_url") or item.get("url")
            else:
                val = item
            if val:
                urls.append(val)
    if not urls and order.authorizations:
        urls = [getattr(auth, "uri", str(auth)) for auth in order.authorizations if auth]
    return urls

def count_dots(s):
    return s.count('.')
